package com.dci.exception;

import com.dci.result.ExceptionResultInfo;
import com.dci.result.ParamException;
import com.dci.result.ResultInfo;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;

/**
 * All rights Reserved, Designed By www.chinadci.com
 *
 * @version V1.0
 * @Title: null.java
 * @Package com.dci.exception
 * @Description: 全局异常处理器
 * @author: dci-许高峰
 * @date: 2018/12/20 13:35
 * @Copyright: 2018 www.chinadci.com. All rights reserved.
 * 注意：本内容仅限于广州城市信息研究所南京分公司内部传阅，禁止外泄以及用于其他的商业目
 */
@ControllerAdvice
public class ExceptionResolverCustom implements HandlerExceptionResolver {

    @Resource
    private HttpMessageConverter<ExceptionResultInfo> jsonMessageConverter;

    /**
     * 前端控制器调用此方法执行异常处理
     * handler，执行的controller类就包装一个方法（对应url的方法）
     *
     * @param httpServletRequest
     * @param httpServletResponse
     * @param o
     * @param e
     * @return
     */
    @Override
    public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
        //输出异常信息
        e.printStackTrace();
        //转成springmvc底层对象（就是对controller方法的封装对象，只有一个方法）
        HandlerMethod handlerMethod = (HandlerMethod) o;
        //取出方法
        Method method = handlerMethod.getMethod();
        //判断是否返回json
        //只要方法上面有responsebody注解表示返回json
        //查找method是否有responsebody注解
        ResponseBody responseBody = AnnotationUtils.findAnnotation(method, ResponseBody.class);
        if (responseBody != null) {
            // 将异常信息转为json输出
            return this.resolveJsonException(httpServletResponse, httpServletRequest, o, e);
        }
        //将异常信息在异常页面显示
        //解析异常
        ExceptionResultInfo exceptionResultInfo = resolveExceptionCustom(e);
        //将异常信息在页面上显示
        httpServletRequest.setAttribute("exceptionResultInfo", exceptionResultInfo.getResultInfo());
        //转向错误页面
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("exceptionResultInfo", exceptionResultInfo.getResultInfo());
        // 逻辑视图名
        modelAndView.setViewName("/base/error");
        return modelAndView;
    }

    /**
     * 解析异常信息
     *
     * @param ex
     * @return
     */
    private ExceptionResultInfo resolveExceptionCustom(Exception ex) {
        ResultInfo resultInfo = null;
        if (ex instanceof ExceptionResultInfo) {
            // 抛出自定义异常信息
            resultInfo = ((ExceptionResultInfo) ex).getResultInfo();
        } else if (ex instanceof ParamException) {
            resultInfo = ((ParamException) ex).getResultInfo();
        } else {
            // 重新构造未知 错误异常
            resultInfo = new ResultInfo();
            resultInfo.setType(ResultInfo.TYPE_RESULT_FAIL);
            resultInfo.setMessage("未知错误！");
        }
        return new ExceptionResultInfo(resultInfo);
    }

    /**
     * 将异常信息转为json输出
     *
     * @param response
     * @param request
     * @param handler
     * @param ex
     * @return
     */
    private ModelAndView resolveJsonException(HttpServletResponse response, HttpServletRequest request, Object handler, Exception ex) {
        //解析异常
        ExceptionResultInfo exceptionResultInfo = resolveExceptionCustom(ex);
        HttpOutputMessage outputMessage = new ServletServerHttpResponse(response);
        try {
            jsonMessageConverter.write(exceptionResultInfo, MediaType.APPLICATION_JSON, outputMessage);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return new ModelAndView();
    }

    public void setJsonMessageConverter(HttpMessageConverter<ExceptionResultInfo> jsonMessageConverter) {
        this.jsonMessageConverter = jsonMessageConverter;
    }
}
